#! /usr/bin/env python
# -*- coding: utf-8 -*-

try:
    import gtk
    import gobject
    from gtk import gdk
except:
    raise SystemExit

import pygtk
if gtk.pygtk_version < (2, 0):
    print "PyGtk 2.0 or later required for this widget"
    raise SystemExit

from math import pi, sqrt
from plane import Plane

class GTKStereo(gtk.DrawingArea):
    ''' A class for drawing orthographis projection in gtk applocation
    '''
    def __init__(self, fdensity):
       gtk.DrawingArea.__init__(self)
       self.connect("expose_event", self.expose)
       self.isolines_coords = self.isolines_prepare(fdensity)
       self.points=[]        
       self.pairs = []
       self.curpair = []

    def expose(self, widget, event):
        self.context = widget.window.cairo_create()
        
        # set a clip region for the expose event
        self.context.rectangle(event.area.x, event.area.y,
                               event.area.width, event.area.height)
        self.context.clip()
        
        self.width, self.height = event.area.width, event.area.height
        self.radius = min(self.width / 2, self.height / 2)-5
        self.centr_x, self.centr_y = self.width / 2, self.height / 2
        
        self.draw(self.context)
    
    def draw(self, context):
        rect = self.get_allocation()
        x = rect.x + rect.width 
        y = rect.y + rect.height

        
        context.set_source_rgb (1, 1, 1)
        context.set_line_width (0.5)
        context.move_to (0,0)
        context.line_to (x,0)
        context.line_to (x,y)
        context.line_to (0,y)
        context.line_to (0,0)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        
        self.draw_frame(self.context, self.radius, self.centr_x, self.centr_y)
        self.draw_isolines(self.context, self.isolines_coords, self.radius, self.centr_x, self.centr_y, self.lx, self.ly)
        if self.curpair != []:
            a = Plane(self.curpair[0][0], 90 - self.curpair[0][1])
            b = Plane(self.curpair[1][0], 90 - self.curpair[1][1])
            c = a.get_perpendicular_between(b)
            self.draw_greatcirckle(context,c.normal())
            self.draw_arrows(context, self.curpair)
        for pair in self.pairs:
            pln = Plane (pair[0][0],pair[0][1])
            pln = pln.normal()
            x, y = pln.cos3,  (-1) * pln.cos1
            x, y = x*self.radius + self.centr_x, y*self.radius + self.centr_y
            self.draw_point(context,x,y,[0,0,0])            
            pln = Plane (pair[1][0],pair[1][1])
            pln = pln.normal()
            x, y = pln.cos3,  (-1) * pln.cos1
            x, y = x*self.radius + self.centr_x, y*self.radius + self.centr_y
            self.draw_point(context,x,y,[0,0,0])            
        for point in self.points:
            pln = Plane (point[0],point[1])
            pln = pln.normal()
            x, y = pln.cos3,  (-1) * pln.cos1
            x, y = x*self.radius + self.centr_x, y*self.radius + self.centr_y
            self.draw_point(context,x,y,[0,0,0])            
        
    def draw_frame(self, context, r, cx, cy):
        context.arc(cx, cy, r, 0, 2 * pi)        
        context.set_source_rgb(0, 0, 0)
        context.set_line_width(1)
        context.stroke()
        context.move_to(cx, cy-r)
        context.line_to(cx, cy-r-5)
        context.stroke()
        context.move_to(cx-4, cy); context.line_to(cx+4, cy); context.stroke()
        context.move_to(cx, cy-4); context.line_to(cx, cy+4); context.stroke()
        
    def draw_isolines(self, context, coords, r, cx, cy, lx, ly):
        for cell in coords:        
            for edge in cell:
                y1,x1 = edge[1], edge[2]
                y2,x2 = edge[3], edge[4]
                x1_,y1_ = cx-(x1*2)*r/lx+r,cy-r+(y1*2)*r/ly        
                x2_,y2_ = cx-(x2*2)*r/lx+r,cy-r+(y2*2)*r/ly
                col = [0,0,0]
                self.draw_line(context, x1_, y1_, x2_, y2_, col, linewidth=0.4)
    
    def draw_point(self, ctx, x, y, col, linewidth=1, r=3):
        ctx.set_source_rgb(col[0], col[1], col[2])
        ctx.set_line_width (linewidth)
        ctx.move_to(x + r, y)
        ctx.arc(x, y, r, 0, 2 * pi)    
        ctx.fill_preserve()
        ctx.set_source_rgb(1,1,1)
        ctx.stroke() 
    
    def draw_line(self, context, x1, y1, x2, y2, col, linewidth=1):        
        context.set_source_rgb(col[0], col[1], col[2])
        context.set_line_width (linewidth)
        context.move_to(x1, y1)
        context.line_to(x2, y2)        
        context.stroke()
    
    def draw_greatcirckle(self, context, plane):
        a = Plane(plane[0], plane[1])
        if plane[1]==90:
            x1,y1=self.get_coords(a.rotated(90))
            x2,y2=self.get_coords(a.rotated(-90))
            self.draw_line(context, x1,y1,x2,y2,[.5,.2,.2])
            return 
        points = []
        for angle in range(-89, 89, 5):
            x,y = self.get_coords(a.rotated(angle))
            x, y = x*self.radius + self.centr_x, y*self.radius + self.centr_y
            points.append([x,y])
        for i in range(len(points)-1):
            self.draw_line(context, points[i][0],points[i][1],points[i+1][0],points[i+1][1], 
                         [.8,.8,.8])
    
    def draw_arrows(self, context, planes):
        al = 20 # length of an arrow in degrees
        a = Plane(planes[0][0], 90 - planes[0][1])
        b = Plane(planes[1][0], 90 - planes[1][1])
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        cb = c.return_angle_between(b)
        points=[]
        for angle in range (ca, ca + al, 5):
            x,y = self.get_coords(c.rotated(angle))
            x, y = x*self.radius + self.centr_x, y*self.radius + self.centr_y
            points.append([x,y])
        for i in range(len(points)-1):
            self.draw_line(context, points[i][0],points[i][1],points[i+1][0],points[i+1][1], 
                         [.1,.1,.1])
        points=[]
        for angle in range (cb, cb + al, 5):
            x,y = self.get_coords(c.rotated(-1*angle))
            x, y = x*self.radius + self.centr_x, y*self.radius + self.centr_y
            points.append([x,y])
        for i in range(len(points)-1):
            self.draw_line(context, points[i][0],points[i][1],points[i+1][0],points[i+1][1], 
                         [.1,.1,.1])


    def isolines_prepare(self, fdensity):        
        import isolines       
        
        # list of intervals
        intervals= [x/100.0 for x in range(0,100,10)]
        
        # doing isolines
        prepared_matrix=isolines.prepare_matrix(fdensity)    
        calculated_dots=isolines.calculate_dots(prepared_matrix,intervals)
        coords=isolines.calculate_coords(calculated_dots)
        
        self.lx, self.ly = len(fdensity), len(fdensity[0])
        return coords
    
    def get_widget_coords(self, x, y):
        cos3=(x-self.centr_x)/self.radius
        cos1=(self.centr_y-y)/self.radius            
        try:
            cos2 = sqrt(1-cos1**2-cos3**2) 
            a = Plane(0,0)
            a.define_by_normal_cos(cos1,cos2,cos3)
            return a.dir, a.dip        
        except:
            return None, None

    def put_a_pole(self, dr, dp):       
        self.points.append([dr,dp])

    def get_coords(self, pln, hemisphere = 'lower'):
        if (hemisphere == 'lower' and pln.cos2 < 0) or \
                                (hemisphere == 'upper' and pln.cos2 > 0) :
            return pln.cos3,  (-1) * pln.cos1
        else:
            return (-1) * pln.cos3, pln.cos1
